package ch.ethz.fcl.metrobuzz.spatialindex.object2d;

import org.lwjgl.opengl.GL11;

import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.tools.MBTools;

/*
 * zw: a line segment contains two 2d points
 */
public class LineSegment implements IObject2D {
	private Point2D start;
	private Point2D end;

	public LineSegment(Point2D point1, Point2D point2) {
		this.start = new Point2D(point1.getX(), point1.getY());
		this.end = new Point2D(point2.getX(), point2.getY());
	}

	public Point2D intersects(LineSegment line) {
		return intersects(this, line);
	}

	public float distance() {
		return start.distance(end);
	}

	// returns the intersection point of two line segments
	// reference http://paulbourke.net/geometry/lineline2d/
	public static Point2D intersects(LineSegment line1, LineSegment line2) {
		Point2D p1 = line1.getPoints()[0];
		Point2D p2 = line1.getPoints()[1];
		Point2D p3 = line2.getPoints()[0];
		Point2D p4 = line2.getPoints()[1];
		float numerator12 = (p4.getX() - p3.getX()) * (p1.getY() - p3.getY())
				- (p4.getY() - p3.getY()) * (p1.getX() - p3.getX());
		float denominator12 = (p4.getY() - p3.getY()) * (p2.getX() - p1.getX())
				- (p4.getX() - p3.getX()) * (p2.getY() - p1.getY());

		float numerator34 = (p2.getX() - p1.getX()) * (p1.getY() - p3.getY())
				- (p2.getY() - p1.getY()) * (p1.getX() - p3.getX());
		float denominator34 = (p4.getY() - p3.getY()) * (p2.getX() - p1.getX())
				- (p4.getX() - p3.getX()) * (p2.getY() - p1.getY());

		if (denominator12 == 0 || denominator34 == 0)
			return null;
		else if (numerator12 / denominator12 >= 0
				&& numerator12 / denominator12 < 1
				&& numerator34 / denominator34 >= 0
				&& numerator34 / denominator34 < 1) {
			float u = numerator12 / denominator12;
			float x = p1.getX() + u * (p2.getX() - p1.getX());
			float y = p1.getY() + u * (p2.getY() - p1.getY());
			return new Point2D(x, y);
		}
		return null;
	}

	public Point2D intersectAsLine(LineSegment line2) {
		Point2D p1 = start;
		Point2D p2 = end;
		Point2D p3 = line2.getPoints()[0];
		Point2D p4 = line2.getPoints()[1];
		float numerator12 = (p4.getX() - p3.getX()) * (p1.getY() - p3.getY())
				- (p4.getY() - p3.getY()) * (p1.getX() - p3.getX());
		float denominator12 = (p4.getY() - p3.getY()) * (p2.getX() - p1.getX())
				- (p4.getX() - p3.getX()) * (p2.getY() - p1.getY());

		float numerator34 = (p2.getX() - p1.getX()) * (p1.getY() - p3.getY())
				- (p2.getY() - p1.getY()) * (p1.getX() - p3.getX());
		float denominator34 = (p4.getY() - p3.getY()) * (p2.getX() - p1.getX())
				- (p4.getX() - p3.getX()) * (p2.getY() - p1.getY());

		if (denominator12 == 0 || denominator34 == 0)
			return null;

		else if (numerator12 / denominator12 >= -50
				&& numerator12 / denominator12 < 50
				&& numerator34 / denominator34 >= -50
				&& numerator34 / denominator34 < 50) {
			float u = numerator12 / denominator12;
			float x = p1.getX() + u * (p2.getX() - p1.getX());
			float y = p1.getY() + u * (p2.getY() - p1.getY());
			return new Point2D(x, y);
		}
		return null;
	}

	// find the 3d pointof p in line p1, p2
	public static Point3D pointIn3D(Point2D p2d, Point3D p1, Point3D p2) {
		if (p2d == null)
			return null;

		float ux = (p2d.getX() - p1.getX()) / (p2.getX() - p1.getX());
		float uy = (p2d.getY() - p1.getY()) / (p2.getY() - p1.getY());

		MBTools.debug("ux " + ux + " uy " + uy + " difference " + (ux - uy)
				/ ux, false);
		// ux, uy difference is too big, consider not
		// on the same line
		if ((ux - uy) / ux > 0.01)
			return null;

		float z = p1.getZ() + ux * (p2.getZ() - p1.getZ());
		MBTools.debug(z + "z", false);
		return new Point3D(p2d.getX(), p2d.getY(), z);
	}

	public float distance(Point2D p) {
		float dx = start.getX() - end.getX();
		float dy = start.getY() - end.getY();

		double denominator = Math.sqrt(dx * dx + dy * dy);
		double numerator = Math.abs(dy * p.getX() - dx * p.getY()
				+ start.getX() * end.getY() - start.getY() * end.getX());

		return (float) (numerator / denominator);
	}

	public Point2D[] getPoints() {
		Point2D[] points = { start, end };
		return points;
	}

	@Override
	public boolean intersects(Rectangle rec) {
		if (!getBound().intersects(rec))
			return false;
		else {
			Point2D p1 = new Point2D(rec.getMinX(), rec.getMinY());
			Point2D p2 = new Point2D(rec.getMinX(), rec.getMaxY());
			Point2D p3 = new Point2D(rec.getMaxX(), rec.getMinY());
			Point2D p4 = new Point2D(rec.getMaxX(), rec.getMaxY());

			return intersects(new LineSegment(p1, p2)) != null
					|| intersects(new LineSegment(p1, p3)) != null
					|| intersects(new LineSegment(p2, p4)) != null
					|| intersects(new LineSegment(p3, p4)) != null;
		}
	}

	@Override
	public boolean contains(Rectangle rec) {
		return false;
	}

	@Override
	public Rectangle getBound() {
		return new Rectangle(start, end);
	}

	@Override
	public void render() {
		GL11.glBegin(GL11.GL_LINES);
		GL11.glVertex3f(start.getX(), start.getY(), 0);
		GL11.glVertex3f(end.getX(), end.getY(), 0);
		GL11.glEnd();
	}

	@Override
	public boolean contains(Point2D point) {
		return false;
	}
}